﻿using Abp.Application.Services;
using Abp.Events.Bus;
using Abp.Runtime.Caching;
using Abp.Runtime.Session;
using Newtonsoft.Json;
using RMM.Core.Orders;
using RMM.Core.UserService;
using RMM.FrameWork;
using RMM.WeChatPay.Dtos;
using RMM.WeChats.Events;
using RMM.WeChats.Models;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.AdvancedAPIs.OAuth;
using Senparc.Weixin.MP.TenPayLibV3;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace RMM.WeChats
{
    public class WeChatAppService : ApplicationService, IWeChatAppService
    {
        private static TenPayV3Info _tenPayV3Info;
        public static TenPayV3Info TenPayV3Info
        {
            get
            {
                if (_tenPayV3Info == null)
                {
                    try
                    {
                        _tenPayV3Info =
                            TenPayV3InfoCollection.Data[ConfigurationManager.AppSettings["TenPayV3_MchId"]];
                    }
                    catch (Exception ex)
                    {

                        throw;
                    }
                }
                return _tenPayV3Info;
            }
        }
        private static object access_token_lock = new object();
        private readonly string AppId = ConfigurationManager.AppSettings["AppId"];
        private readonly string AppSecret = ConfigurationManager.AppSettings["AppSecret"];

        private readonly IUserService _userService;
        private readonly IOrderService _orderService;
        private readonly ICacheManager _cacheManager;


        public IAbpSession abpSession { get; set; }
        public IEventBus EventBus { get; set; }


        public WeChatAppService(IUserService userService
            , IOrderService orderService
            , ICacheManager cacheManager)
        {
            _userService = userService;
            _orderService = orderService;
            abpSession = NullAbpSession.Instance;
            EventBus = NullEventBus.Instance;
            _cacheManager = cacheManager;
        }

        /// <summary>
        /// 统一下单
        /// </summary>
        /// <param name="orderCode"></param>
        /// <returns></returns>
        public async Task<ResultDto> JsApiUnifiedOrder(string orderCode)
        {
            var userId = abpSession.UserId ?? 0;

            var order = await _orderService.FirstOrDefaultAsync(p => p.OrderCode == orderCode);
            if (order == null)
            {
                ResultDto.Fail("订单不存在");
            }
            
            var openId = (await _userService.FirstOrDefaultAsync(p => p.Id == order.UserId)).OpenId;

            int orderTotalFee = Convert.ToInt32(order.Price * 100);
            var productName = $"RMM{order.OrderCode}";
            var timeStamp = TenPayV3Util.GetTimestamp();
            var nonceStr = TenPayV3Util.GetNoncestr();
            var xmlInfo = new TenPayV3UnifiedorderRequestData();
            try
            {
                xmlInfo = new TenPayV3UnifiedorderRequestData(
                    appId: TenPayV3Info.AppId, 
                    mchId: TenPayV3Info.MchId, 
                    body: productName,
                    outTradeNo: orderCode,
                    totalFee: orderTotalFee,
                    spbillCreateIp: CommonHelper.GetIP(),
                    notifyUrl: TenPayV3Info.TenPayV3Notify,
                    tradeType: TenPayV3Type.JSAPI,
                    openid: openId,
                    key: TenPayV3Info.Key,
                    nonceStr: nonceStr);
                Logger.Error(JsonConvert.SerializeObject(xmlInfo));
            }
            catch (Exception ex)
            {

                throw;
            }


            try
            {
                var result = TenPayV3.Unifiedorder(xmlInfo);
                var prepayId = result.prepay_id;
                if (!result.IsResultCodeSuccess())
                {
                    return ResultDto.Fail(JsonConvert.SerializeObject(new WechatPayParamModel()
                    {
                        Status = false,
                        msg = result.ResultXml
                    }));
                }

                //设置支付参数
                RequestHandler paySignReqHandler = new RequestHandler(null);
                paySignReqHandler.SetParameter("appId", TenPayV3Info.AppId);
                paySignReqHandler.SetParameter("timeStamp", timeStamp);
                paySignReqHandler.SetParameter("nonceStr", nonceStr);
                paySignReqHandler.SetParameter("package", $"prepay_id={prepayId}");
                paySignReqHandler.SetParameter("signType", "MD5");
                
                var paySign = paySignReqHandler.CreateMd5Sign("key", TenPayV3Info.Key);

                var successModel = new WechatPayParamModel()
                {
                    appId = TenPayV3Info.AppId,
                    timeStamp = timeStamp,
                    nonceStr = nonceStr,
                    packageValue = $"prepay_id={prepayId}",
                    paySign = TenPayV3.GetJsPaySign(AppId, timeStamp, nonceStr, $"prepay_id={prepayId}", TenPayV3Info.Key),
                    msg = "下单成功",
                    Status = true
                };

                return ResultDto.Success(JsonConvert.SerializeObject(successModel));
            }
            catch (Exception ex)
            {
                var model = new WechatPayParamModel()
                {
                    msg = "下单失败,请重试",
                    Status = false
                };
                return ResultDto.Fail(JsonConvert.SerializeObject(model));
            }
        }


        /// <summary>
        /// 微信支付回调地址
        /// </summary>
        /// <returns></returns>
        public async Task<object> Notify()
        {
            Logger.Info("微信支付回调");
            var responseHandler = new ResponseHandler(null);
            var return_code = responseHandler.GetParameter("return_code");
            var return_msg = responseHandler.GetParameter("return_msg");
            var tradeNo = responseHandler.GetParameter("out_trade_no");
            var order = await _orderService.FirstOrDefaultAsync(p => p.OrderCode == tradeNo);
            if (order == null)
            {
                throw new Exception("订单号不一致");
            }


            string res = null;
            responseHandler.SetKey(TenPayV3Info.Key);
            // 验证请求是否微信发过来
            if (responseHandler.IsTenpaySign())
            {
                Logger.Info($"微信支付回调orderCode={tradeNo}");
                res = "SUCCESS";
                var orderNo = tradeNo;
                var input = new OrderPaidInput();
                var wechatResult = new WechatPayResult()
                {
                    OrderNo = orderNo,
                    OpenId = responseHandler.GetParameter("openid"),
                    TransactionNo = responseHandler.GetParameter("transaction_id")
                };

                int.TryParse(responseHandler.GetParameter("total_fee"), out int amount);
                int.TryParse(responseHandler.GetParameter("cash_fee"), out int cashAmount);

                DateTime paidTime;
                DateTime.TryParseExact(responseHandler.GetParameter("time_end"), "yyyyMMddHHmmss", null, System.Globalization.DateTimeStyles.None, out paidTime);
                wechatResult.Amount = (decimal)(amount / 100.00);
                wechatResult.CashAmount = (decimal)(cashAmount / 100.00);
                wechatResult.PaidTime = paidTime;

                input.WechatPayResult = wechatResult;

                //修改表状态为-已完成
                await EventBus.TriggerAsync(new WeChatNotifyEventData()
                {
                    OrderCode =tradeNo
                });
            }
            else
            {
                res = "FAIL";
            }

            string xml = string.Format(@"
                <xml>
                    <return_code><![CDATA[{0}]]></return_code>
                    <return_msg><![CDATA[{1}]]></return_msg>
                </xml>", res, return_msg);

            return xml;
        }

        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <returns></returns>
        public async Task<string> GetAccessTokenAsync()
        {
            var cache = _cacheManager.GetCache("WechatAccessToken");
            var result = await cache.GetAsync("AccessToken", () => {
                lock (access_token_lock)//防止多取
                {
                    var token = cache.GetOrDefault("AccessToken") as AccessToken;
                    if (token == null)
                    {
                        token =  GetAccessToken();
                    }
                    return Task.FromResult(token);
                }
            });
            return result.access_token;
        }

        private AccessToken GetAccessToken()
        {
            var url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
            url = string.Format(url, AppId, AppSecret);
            var resultJson = CommonHelper.Get<AccessToken>(url);
            return resultJson;
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="openId"></param>
        /// <returns></returns>
        public async Task<WechatUserInfo> GetUserInfoAsync(string openId)
        {
            var token = await GetAccessTokenAsync();
            var userInfo = GetUserInfoAsync(token, openId);
            return userInfo;
        }

        public WechatUserInfo GetUserInfoAsync(string token,string openId)
        {
            var url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang=zh_CN";
            url = string.Format(url, token, openId);
            var result = CommonHelper.Get<WechatUserInfo>(url);
            return result;
        }

        public async Task<long?> GetUserIdByOpenOrUnionId(string id)
        {
            var user = await _userService.FirstOrDefaultAsync(p => p.OpenId == id || p.UnionID == id);
            return user?.Id;
        }

        public async Task<string> GetOpenIdByUserId(long userId)
        {
            var user = await _userService.FirstOrDefaultAsync(p => p.Id == userId);
            return user?.OpenId;
        }


        public async Task ClearTocken()
        {
           await _cacheManager.GetCache("WechatAccessToken").ClearAsync();
        }
    }
}
